﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using H2.Streams;
using System.IO;
using System.Windows.Forms;
using Mystery.SCNR;

namespace Mystery
{
    public class Scenario
    {
        const int HeaderSize = 992;
        public const string MultiplayerDataTagPath = @"scenarios\solo\multiplayer_data";

        //public List<Placement> Bipeds = new List<Placement>();
        public List<Scenery> Scenery = new List<Scenery>();
        //public List<Placement> Weapons = new List<Placement>();
        //public List<Placement> Vehicles = new List<Placement>();

        public List<NetgameFlag> NetgameFlags = new List<NetgameFlag>();
        public List<NetgameEquipment> NetgameEquipment = new List<NetgameEquipment>();
        public List<StartingEquipment> StartingEquipment = new List<StartingEquipment>();

        public List<TriggerVolume> TriggerVolumes = new List<TriggerVolume>();
        public List<KillTriggers> KillTriggers = new List<KillTriggers>();

        public List<PlayerStartingLocation> PlayerStartingLocations = new List<PlayerStartingLocation>();

        public SpawnData SpawnData;

        public LevelData LevelData;

        public void Output(List<string> outputFiles, string outputDir)
        {
            string filename = Path.Combine(outputDir, MultiplayerDataTagPath);
            outputFiles.Add(filename);

            if (Directory.Exists(Path.GetDirectoryName(filename)) == false)
                Directory.CreateDirectory(Path.GetDirectoryName(filename));

            FileStream fs = new FileStream(filename, FileMode.Create);
            BaseStream rs = new BaseStream(fs, null);

            //Calculate Offsets
            int offset = HeaderSize;

            int sceneryPointer = offset;
            offset += Scenery.Count * Mystery.Scenery.Size;

            List<int> sceneryPathfindingPointers = new List<int>();
            foreach (Scenery s in Scenery)
            {
                sceneryPathfindingPointers.Add(offset);
                if (s.PathfindingReferences == null) continue;
                offset += s.PathfindingReferences.Length * Mystery.Scenery.PathfindingReference.Size;
            }

            int playerStartingLocationsCount = Math.Min(PlayerStartingLocations.Count, 256);
            int playerStartingLocationSkipCount = PlayerStartingLocations.Count - playerStartingLocationsCount;
            int playerStartPointer = offset;
            offset += playerStartingLocationsCount * Mystery.PlayerStartingLocation.Size;

            int triggerVolumePointer = offset;
            offset += TriggerVolumes.Count * Mystery.TriggerVolume.Size;

            int netgameFlagsPointer = offset;
            offset += NetgameFlags.Count * Mystery.NetgameFlag.Size;

            int netgameEquipmentCount = Math.Min(NetgameEquipment.Count, 100);
            int netgameEquipmentSkipCount = NetgameEquipment.Count - netgameEquipmentCount;
            int netgameEquipmentPointer = offset;
            offset += netgameEquipmentCount * Mystery.NetgameEquipment.Size;

            int startingEquipmentPointer = offset;
            offset += StartingEquipment.Count * Mystery.StartingEquipment.Size;

            int killTriggersPointer = offset;
            offset += KillTriggers.Count * Mystery.KillTriggers.Size;


            int spawnDataPointer = offset;
            if (SpawnData != null)
                offset += Mystery.SCNR.SpawnData.Size;

            int staticRespawnZonesPointer = offset;
            if (SpawnData != null && SpawnData.StaticRespawnZones != null)
                offset += SpawnData.StaticRespawnZones.Length * Mystery.SCNR.SpawnData.StaticZone.Size;

            int staticInitialSpawnZonesPointer = offset;
            if (SpawnData != null && SpawnData.StaticInitialSpawnZones != null)
                offset += SpawnData.StaticInitialSpawnZones.Length * Mystery.SCNR.SpawnData.StaticZone.Size;


            int levelDataPointer = offset;
            if (LevelData != null)
                offset += Mystery.SCNR.LevelData.Size;

            int campaignLevelDataPointer = offset;
            if (LevelData != null && LevelData.CampaignLevel != null)
                offset += Mystery.SCNR.LevelData.CampaignLevelData.Size;

            int multiplayerLevelDataPointer = offset;
            if (LevelData != null && LevelData.MultiplayerLevel != null)
                offset += Mystery.SCNR.LevelData.MultiplayerLevelData.Size;

            //Write Data
            rs.Write(new byte[HeaderSize]);

            foreach (Scenery o in Scenery)
                o.WriteTo(rs);

            foreach (Scenery o in Scenery)
            {
                if (o.PathfindingReferences == null) continue;
                foreach (Mystery.Scenery.PathfindingReference p in o.PathfindingReferences)
                    p.WriteTo(rs);
            }

            int skipValue = -1;
            int count = 0;
            foreach (PlayerStartingLocation o in PlayerStartingLocations)
            {
                skipValue++;
                if (playerStartingLocationSkipCount > 0 && (skipValue & 1) == 1)
                {
                    playerStartingLocationSkipCount--;
                    continue;
                }
                o.WriteTo(rs);
                count++;
                if (count == playerStartingLocationsCount) break;
            }

            foreach (TriggerVolume o in TriggerVolumes)
                o.WriteTo(rs);

            foreach (NetgameFlag o in NetgameFlags)
                o.WriteTo(rs);

            skipValue = -1;
            count = 0;
            foreach (NetgameEquipment o in NetgameEquipment)
            {
                skipValue++;
                if (netgameEquipmentSkipCount > 0 && (skipValue & 1) == 1)
                {
                    netgameEquipmentSkipCount--;
                    continue;
                }
                o.WriteTo(rs);
                count++;
                if (count == netgameEquipmentCount) break;
            }

            foreach (StartingEquipment o in StartingEquipment)
                o.WriteTo(rs);

            foreach (KillTriggers o in KillTriggers)
                o.WriteTo(rs);

            if (SpawnData != null)
                SpawnData.WriteTo(rs);

            if (LevelData != null)
                LevelData.WriteTo(rs);

            rs.Flush();
            rs.Close();

            //Output XML
            XMLInfo xml = new XMLInfo(filename + ".xml");
            xml.TagName = MultiplayerDataTagPath;
            xml.TagClass = "scnr";
            xml.Size = offset;

            xml.WriteHeader();
            
            //Write Reflexives
            if (Scenery.Count > 0)
                xml.WriteReflexive(Mystery.Scenery.Offset, Scenery.Count, Mystery.Scenery.Size, sceneryPointer);

            for (int i = 0; i < Scenery.Count; i++)
            {
                if (Scenery[i].PathfindingReferences != null && Scenery[i].PathfindingReferences.Length > 0)
                {
                    xml.WriteReflexive(
                        sceneryPointer + (Mystery.Scenery.Size * i) + Mystery.Scenery.PathfindingReference.Offset,
                        Scenery[i].PathfindingReferences.Length, Mystery.Scenery.PathfindingReference.Size, sceneryPathfindingPointers[i]);
                }
            }

            if (PlayerStartingLocations.Count > 0)
                xml.WriteReflexive(PlayerStartingLocation.Offset, playerStartingLocationsCount, Mystery.PlayerStartingLocation.Size, playerStartPointer);
            if (TriggerVolumes.Count > 0)
                xml.WriteReflexive(TriggerVolume.Offset, TriggerVolumes.Count, Mystery.TriggerVolume.Size, triggerVolumePointer);
            if (NetgameFlags.Count > 0)
                xml.WriteReflexive(NetgameFlag.Offset, NetgameFlags.Count, Mystery.NetgameFlag.Size, netgameFlagsPointer);
            if (NetgameEquipment.Count > 0)
                xml.WriteReflexive(Mystery.NetgameEquipment.Offset, netgameEquipmentCount, Mystery.NetgameEquipment.Size, netgameEquipmentPointer);
            if (StartingEquipment.Count > 0)
                xml.WriteReflexive(Mystery.StartingEquipment.Offset, StartingEquipment.Count, Mystery.StartingEquipment.Size, startingEquipmentPointer);
            if (KillTriggers.Count > 0)
                xml.WriteReflexive(Mystery.KillTriggers.Offset, KillTriggers.Count, Mystery.KillTriggers.Size, killTriggersPointer);
            
            if (SpawnData != null)
                xml.WriteReflexive(Mystery.SCNR.SpawnData.Offset, 1, Mystery.SCNR.SpawnData.Size, spawnDataPointer);
            if (SpawnData != null && SpawnData.StaticRespawnZones != null)
                xml.WriteReflexive(spawnDataPointer + Mystery.SCNR.SpawnData.StaticRespawnZoneOffset, SpawnData.StaticRespawnZones.Length, Mystery.SCNR.SpawnData.StaticZone.Size, staticRespawnZonesPointer);
            if (SpawnData != null && SpawnData.StaticInitialSpawnZones != null)
                xml.WriteReflexive(spawnDataPointer + Mystery.SCNR.SpawnData.StaticInitialSpawnZoneOffset, SpawnData.StaticInitialSpawnZones.Length, Mystery.SCNR.SpawnData.StaticZone.Size, staticInitialSpawnZonesPointer);
            
            if (LevelData != null)
                xml.WriteReflexive(Mystery.SCNR.LevelData.Offset, 1, Mystery.SCNR.LevelData.Size, levelDataPointer);
            if (LevelData != null && LevelData.CampaignLevel != null)
                xml.WriteReflexive(levelDataPointer + Mystery.SCNR.LevelData.CampaignLevelData.Offset, 1, Mystery.SCNR.LevelData.CampaignLevelData.Size, campaignLevelDataPointer);
            if (LevelData != null && LevelData.MultiplayerLevel != null)
                xml.WriteReflexive(levelDataPointer + Mystery.SCNR.LevelData.MultiplayerLevelData.Offset, 1, Mystery.SCNR.LevelData.MultiplayerLevelData.Size, multiplayerLevelDataPointer);

            //Write Indents
            netgameEquipmentSkipCount = NetgameEquipment.Count - netgameEquipmentCount;
            skipValue = -1;
            count = 0;
            for (int i = 0; i < NetgameEquipment.Count; i++)
            {
                skipValue++;
                if (netgameEquipmentSkipCount > 0 && (skipValue & 1) == 1)
                {
                    netgameEquipmentSkipCount--;
                    continue;
                }
                
                if (NetgameEquipment[i].Collection != null)
                {
                    xml.WriteIdent(netgameEquipmentPointer + (Mystery.NetgameEquipment.Size * count) + 92,
                        NetgameEquipment[i].Collection.ClassString, NetgameEquipment[i].Collection.Path);
                }

                count++;
                if (count == netgameEquipmentCount) break;
            }

            for (int i = 0; i < StartingEquipment.Count; i++)
            {
                if (StartingEquipment[i].Collection1 != null) 
                    xml.WriteIdent(startingEquipmentPointer + (Mystery.StartingEquipment.Size * i) + 64,
                        StartingEquipment[i].Collection1.ClassString, StartingEquipment[i].Collection1.Path);
                if (StartingEquipment[i].Collection2 != null)
                    xml.WriteIdent(startingEquipmentPointer + (Mystery.StartingEquipment.Size * i) + 72,
                        StartingEquipment[i].Collection2.ClassString, StartingEquipment[i].Collection2.Path);
            }

            if (LevelData != null)
            {
                xml.WriteIdent(levelDataPointer + 4, LevelData.LevelDescription.ClassString, LevelData.LevelDescription.Path);

                if (LevelData.CampaignLevel != null)
                    xml.WriteIdent(levelDataPointer + LevelData.Size + 12, LevelData.CampaignLevel.PreviewImage.ClassString, LevelData.CampaignLevel.PreviewImage.Path);
                if (LevelData.MultiplayerLevel != null)
                    xml.WriteIdent(levelDataPointer + LevelData.Size + ((LevelData.CampaignLevel != null) ? LevelData.CampaignLevelData.Size : 0) + 8, LevelData.MultiplayerLevel.PreviewImage.ClassString, LevelData.MultiplayerLevel.PreviewImage.Path);
            }

            //TODO: Write String IDs
            //Needed for Scenery possibly

            if (SpawnData != null)
            {
                int startOffset = spawnDataPointer + SpawnData.Size;
                for (int i = 0; i < SpawnData.StaticRespawnZones.Length; i++)
                    xml.WriteStringID(startOffset + (i * SpawnData.StaticZone.Size), SpawnData.StaticRespawnZones[i].Name.ToString());

                startOffset += SpawnData.StaticRespawnZones.Length * SpawnData.StaticZone.Size;
                for (int i = 0; i < SpawnData.StaticInitialSpawnZones.Length; i++)
                    xml.WriteStringID(startOffset + (i * SpawnData.StaticZone.Size), SpawnData.StaticInitialSpawnZones[i].Name.ToString());
            }

            xml.WriteEnd();
            xml.FlushAndClose();
        }
    }
}
